<?php

/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace common\widgets;

use Yii;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Url;
use backend\components\BaseModule;
use mdm\admin\components\Helper as AdminHelper;

/**
 * Menu displays a multi-level menu using nested HTML lists.
 *
 * The main property of Menu is [[items]], which specifies the possible items in the menu.
 * A menu item can contain sub-items which specify the sub-menu under that menu item.
 *
 * Menu checks the current route and request parameters to toggle certain menu items
 * with active state.
 *
 * Note that Menu only renders the HTML tags about the menu. It does do any styling.
 * You are responsible to provide CSS styles to make it look like a real menu.
 *
 * The following example shows how to use Menu:
 *
 * ```php
 * echo ModuleMenu::widget([
 *     'items' => [
 *         // Important: you need to specify url as 'controller/action',
 *         // not just as 'controller' even if default action is used.
 *         ['label' => 'Home', 'url' => ['site/index']],
 *         // 'Products' menu item will be selected as long as the route is 'product/index'
 *         ['label' => 'Products', 'url' => ['product/index'], 'items' => [
 *             ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']],
 *             ['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']],
 *         ]],
 *         ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],
 *     ],
 * ]);
 * ```
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class ModuleMenu extends \yii\base\Widget {

    public $items = [];

    /**
     * @var array list of HTML attributes shared by all menu [[items]]. If any individual menu item
     * specifies its `options`, it will be merged with this property before being used to generate the HTML
     * attributes for the menu item tag. The following special options are recognized:
     *
     * - tag: string, defaults to "li", the tag name of the item container tags.
     *   Set to false to disable container tag.
     *   See also [[\yii\helpers\Html::tag()]].
     *
     * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
     */
    public $itemOptions = ['class' => 'nav-item'];

    /**
     * @var string the template used to render the body of a menu which is a link.
     * In this template, the token `{url}` will be replaced with the corresponding link URL;
     * while `{label}` will be replaced with the link text.
     * This property will be overridden by the `template` option set in individual menu items via [[items]].
     */
    public $linkTemplate = '<a href="{url}" class="nav-link">{icon}{label}{badge}</a>';

    /**
     * @var array the HTML attributes for the menu's container tag. The following special options are recognized:
     *
     * - tag: string, defaults to "ul", the tag name of the item container tags. Set to false to disable container tag.
     *   See also [[\yii\helpers\Html::tag()]].
     *
     * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
     */
    public $options = [];

    /**
     * @var array The values not visiablity
     */
    public $ignoreModules = ['debug', 'gii', 'rbac', 'xhr', 'redactor', 'migration', 'account'];

    /**
     * Renders the menu.
     */
    public function run() {
        $moduleMenus = $this->getMoudleMenus();
        $this->items = ArrayHelper::merge($this->items, $moduleMenus);
        if (empty($this->items)) {
            return;
        }
        $options = $this->options;
        $tag = ArrayHelper::remove($options, 'tag', 'ul');
        $items = $this->renderItems();
        echo Html::tag($tag, $items, $options);
    }

    /**
     * Recursively renders the menu items (without the container tag).
     * @param array $items the menu items to be rendered recursively
     * @return string the rendering result
     */
    protected function renderItems() {
        $lines = [];
        foreach ($this->items as $item) {
            if (isset($item['visible']) && !$item['visible']) {
                continue;
            }
            $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', []));

            $tag = ArrayHelper::remove($options, 'tag', 'li');
            $class = '';
            if ($item['active']) {
                $class = 'active';
            }
            Html::addCssClass($options, $class);

            $menu = $this->renderItem($item);

            $lines[] = Html::tag($tag, $menu, $options);
        }

        return implode("\n", $lines);
    }

    /**
     * Renders the content of a menu item.
     * Note that the container and the sub-menus are not rendered here.
     * @param array $item the menu item to be rendered. Please refer to [[items]] to see what data might be in the item.
     * @return string the rendering result
     */
    protected function renderItem($item) {
        if (!isset($item['url'])) {
            $item['url'] = '';
        }

        if (!isset($item['label'])) {
            $item['label'] = '';
        }
        $item['icon'] = isset($item['icon']) ? $item['icon'] : '';

        $badge = '';
        if (isset($item['badge'])) {
            $badgeCssClass = isset($item['badgeLevel']) ? $item['badgeLevel'] : 'danger';
            $badge = Html::tag('span', $item['badge'], ['class' => 'badge badge-' . $badgeCssClass]);
        }

        $template = ArrayHelper::getValue($item, 'template', $this->linkTemplate);

        return strtr($template, [
            '{url}' => Html::encode(Url::to($item['url'])),
            '{icon}' => '<span class="icon">' . $item['icon'] . '</span>',
            '{label}' => '<span class="title">' . $item['label'] . '</span>',
            '{badge}' => $badge,
        ]);
    }

    public function getMoudleMenus() {
        $modules = Yii::$app->modules;
        $moduleRoutes = $this->moduleRoutes;
        if (empty($modules) || $moduleRoutes === false) {
            return [];
        }
        $menuItems = [];
        foreach ($modules as $moduleName => $module) {
            if (!$moduleName || in_array($moduleName, $this->ignoreModules)) {
                continue;
            }
            $assignment = true;
            if (is_array($module) && isset($module['assignment'])) {
                $assignment = $module['assignment'];
            } elseif (is_object($module) && $module instanceof BaseModule) {
                $assignment = $module->assignment;
            }
            if (!$assignment) {
                continue;
            }
            if (empty($moduleRoutes) || in_array($moduleName, $moduleRoutes)) {
                $icon = Html::tag('i', '', ['class' => Yii::t('module/icon', $moduleName)]);
                $label = Html::tag('span', Yii::t('module/name', $moduleName));
                $menuItems[] = [
                    'icon' => $icon,
                    'label' => $label,
                    'url' => ['/' . $moduleName],
                    'active' => $this->getView()->context->module->id == $moduleName,
                    'options' => [
                        'title' => Yii::t('module/desc', $moduleName),
                    ]
                ];
            }
        }
        return $menuItems;
    }

    public function getModuleRoutes() {
        if (Yii::$app->user->isGuest) {
            return false;
        }
        $userRoutes = array_keys(AdminHelper::getRoutesByUser(Yii::$app->user->id));
        $return = [];
        foreach ($userRoutes as $route) {
            if ($route == '/*') {
                $return = [];
                break;
            }
            $data = explode('/', ltrim($route, '/'));
            $return[] = array_shift($data);
        }
        return array_unique($return);
    }

    protected static function formatDepthArray($userRoutes) {
        foreach ($userRoutes as $route) {
            if ($route == '/*') {
                continue;
            }
            $data = explode('/', ltrim($route, '/'));
            $key = array_search($route, $userRoutes);
            while ($last = array_pop($data)) {
                if ($last == '*') {
                    continue;
                }
                $temp = !empty($data) ? implode('/', $data) . '/' : '';

                if ($key !== false && in_array('/' . $temp . '*', $userRoutes)) {
                    unset($userRoutes[$key]);
                    break;
                }
            }
        }
        return $userRoutes;
    }

}
